<template>
  <span>
    <el-upload v-bind="$attrs"
               :headers="headers"
               :http-request="handleUpload"
               class="upload-box"
               :class="{ disabled: disabled }"
               action="/fileupload"
               list-type="picture-card"
               :accept="acceptFiles.join(',')"
               :limit="limit"
               :file-list="fileList"
               :on-success="handleSuccess"
               :on-preview="handlePreview"
               :on-remove="handleRemove"
               :on-error="handleError"
               :on-exceed="handleExceed"
               :before-upload="beforeUpload"
               :disabled="limit <= fileList.length">
      <i class="el-icon-plus" />
      <div slot="file"
           slot-scope="{ file }">
        <el-image class="el-upload-list__item-thumbnail"
                  :src="file.url"
                  :fit="fit || 'contain'" />
        <el-checkbox v-if="checkable"
                     v-model="file.checked"
                     :true-label="1"
                     :false-label="0"
                     @change="handleCheck" />
        <span class="el-upload-list__item-actions">
          <span class="el-upload-list__item-preview">
            <i class="el-icon-zoom-in"
               @click="handlePreview(file)"></i>
          </span>
          <span class="el-upload-list__item-delete">
            <i class="el-icon-delete"
               @click="handleRemove(file)"></i>
          </span>
        </span>
      </div>
      <div slot="tip"
           class="el-upload__tip">{{ bottomTip }}</div>
    </el-upload>
    <ImagePreview :value="previewImages"
                  :visible.sync="previewing"
                  :startPosition="previewStartPosition" />
  </span>
</template>
<script>
import { uploadPicture } from "@/api/modules/upload";
import ImagePreview from "@/components/basic/ImagePreview.vue";

const DEFAULT_ACCEPT_FILES = [".jpg", ".bmp", ".gif", ".png", ".jpeg"];

function getFileSuffix (fileName = "") {
  const dotIndex = fileName.lastIndexOf(".");
  return fileName.substring(dotIndex, fileName.length);
}

export default {
  name: "ImageUpload",
  components: { ImagePreview },
  props: ["value", "limit", "checkable", "tip", "accept", "fit"],
  inheritAttrs: true,
  data () {
    return {
      fileList: [],
      previewing: false,
      previewStartPosition: 0
    };
  },
  computed: {
    headers () {
      return {};
    },
    disabled () {
      return this.limit <= this.fileList.length;
    },
    acceptFiles () {
      return this.accept?.split(",") || DEFAULT_ACCEPT_FILES;
    },
    bottomTip () {
      return (
        this.tip || `仅支持文件类型${this.acceptFiles.join(",")}/不能超过2MB`
      );
    },
    previewImages () {
      return this.fileList.map((file) => file.url);
    },
    remoteImages () {
      return this.fileList.map((file) => {
        if (file.response?.fileUrl) {
          const { fileUrl: url, fileName: name = file.name } = file.response;
          return { ...file, url, name };
        }
        return file;
      });
    }
  },
  created () {
    this.fileList = [...(this.value || [])];
  },
  methods: {
    checkFileSuffix (fileName) {
      const suffix = getFileSuffix(fileName);
      return this.acceptFiles.includes(suffix);
    },
    checkFileSize (fileSize) {
      return fileSize / 1024 / 1024 <= 2;
    },
    handlePreview (previewFile) {
      this.previewStartPosition = this.fileList.findIndex(
        (file) => previewFile.url === file.url
      );
      if (this.previewStartPosition === -1) {
        this.previewStartPosition = 0;
      }
      this.previewing = true;
    },
    handleCheck () {
      this.$emit("input", this.remoteImages);
    },
    handleExceed () {
      this.$message({
        type: "error",
        message: `最多能上传${this.limit}张图片`
      });
    },
    handleError (err, file, fileList) {
      // TODO: 处理上传失败
      this.$message({ type: "error", message: "图片上传失败" });
      this.handleRemove(file);
    },
    handleRemove (removedFile) {
      this.fileList = this.fileList.filter(
        (file) => file.url !== removedFile.url
      );
      this.$emit("input", this.remoteImages);
    },
    beforeUpload (file) {
      if (!this.checkFileSuffix(file.name)) {
        this.$message({
          type: "error",
          message: `仅支持文件类型${this.acceptFiles.join(",")}/不能超过2MB`
        });
        return false;
      }

      if (!this.checkFileSize(file.size)) {
        this.$message({ type: "error", message: "文件大小不能超过2MB" });
        return false;
      }
    },
    handleSuccess (response, file, fileList) {
      this.fileList = fileList;
      this.$emit("input", this.remoteImages);
    },
    async handleUpload (file) {
      let formData = new FormData();
      formData.append("file", file.file);
      const response = await uploadPicture(formData);
      if (response?.code === 0) {
        return response.data[0];
      } else {
        throw new Error("上传失败");
      }
    }
  }
};
</script>

<style scoped lang="scss">
.upload-box {
  position: relative;
  .el-checkbox {
    position: absolute;
    top: 0px;
    left: 4px;
    line-height: 20px;
  }

  &.disabled {
    /deep/.el-upload--picture-card {
      display: none;
    }
  }
}

::v-deep .el-upload--picture-card {
  width: 102px;
  height: 102px;
  line-height: 108px;
  margin: 0 2px 2px 0;
}

::v-deep .el-upload-list__item .el-image__inner {
  width: 102px;
  height: 102px;
  line-height: 102px;
  margin: 0 2px 2px 0;
}

::v-deep .el-upload-list--picture-card .el-upload-list__item {
  overflow: hidden;
  background-color: var(--prev-bg-white);
  border: 1px solid #c0ccda;
  border-radius: 6px;
  box-sizing: border-box;
  width: 102px;
  height: 102px;
  margin: 0 3px 4px 0;
}
</style>
